//#preprocess
/**
 * AUTO_COPYRIGHT_SUB_TAG
 */
package com.rim.samples.device.push;

//#ifndef HANDHELD_VERSION_42
import java.util.Vector;

import log.Logger;
import net.rim.blackberry.api.homescreen.HomeScreen;
import net.rim.blackberry.api.messagelist.ApplicationIcon;
import net.rim.blackberry.api.messagelist.ApplicationIndicator;
import net.rim.blackberry.api.messagelist.ApplicationIndicatorRegistry;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.DeviceInfo;
import net.rim.device.api.system.EncodedImage;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;
import parser.PushMessageParser;
import screens.Controller;

import com.rim.samples.device.push.cmd.NetworkCommand;
import com.rim.samples.device.push.cmd.RegisterCommand;
import com.rim.samples.device.push.cmd.ResumeCommand;
import com.rim.samples.device.push.cmd.SuspendCommand;
import com.rim.samples.device.push.cmd.UnregisterCommand;
import com.rim.samples.device.push.lib.PushLibFactory;
import com.rim.samples.device.push.lib.PushMessageListener;
import com.rim.samples.device.push.ui.WelcomeScreen;

/**
 * Main controller of the push application.
 * <p>
 * The class performs registration actions, analyzes errors and handles state transitions, uses UI screens to display push
 * messages.
 */
public class PushController {

	private static NetworkCommand currentCommand;
	private static final Object cmdLock = new Object();

	/**
	 * Registers current device and application to receive push messages. This method is called when user selects Register menu
	 * item and should be called in the application event thread.
	 */
	public static void register() {
		if( commandInProgress() ) {
			return;
		}

		// check that configuration is set
		if( invalidSettings() ) {
			Logger.log("Can't subscribe: Invalid push settings");
			return;
		}

		if( !NetworkCoverageManager.hasCoverage() ) {
			int retry = Dialog.ask( Dialog.D_YES_NO, "The BlackBerry smartphone is out of coverage right now. "
					+ "Do you want to schedule automatic re-try when network is accessible?" );
			if( retry == Dialog.NO ) {
				// user will register manually when device has coverage
				Dialog.inform( "Please register when smartphone is in coverage" );
				Logger.log("not enough coverage");
				return;
			}
		}

		// get user credentials
		//        UserCredentialsDialog ucd = new UserCredentialsDialog( null );
		//        if( ucd.doModal() == Dialog.OK ) {
		//            // subscribe user with content provider
		//            String user = ucd.getUsername();
		//            String pass = ucd.getPassword();
		//            // store user name
		//            PersistentStorage.setUsername( user );
		//            // perform the registration
		//            RegisterCommand command = new RegisterCommand( user, pass, PushConfig.isEnterprise() );
		//            executeCommand( command );
		//        }

		// Instead of getting user credentials from the dialog above, use pin (user=password):
		Logger.log("Starting register command");
		String userPass = Integer.toHexString(DeviceInfo.getDeviceId()).toUpperCase();
		PersistentStorage.setUsername( userPass );
		RegisterCommand command = new RegisterCommand( userPass, userPass, PushConfig.isEnterprise() );
		executeCommand( command );

		Logger.log("PUSH: successfully registered RIM");
	}

	/**
	 * Unregisters from receiving push messages
	 */
	public static void unregister() {
		// check that configuration is set and no command in progress
		if( commandInProgress() || invalidSettings() ) {
			return;
		}

		if( !NetworkCoverageManager.hasCoverage() ) {
			int retry = Dialog.ask( Dialog.D_YES_NO, "The BlackBerry smartphone is out of coverage right now. "
					+ "Do you want to schedule automatic re-try when network is accessible?" );
			if( retry == Dialog.NO ) {
				// user will register manually when device has coverage
				Dialog.inform( "Please un-register when smartphone is in coverage" );
				return;
			}
		}

		// get user credentials
		//		UserCredentialsDialog ucd = new UserCredentialsDialog( PersistentStorage.getUsername() );
		//		if( ucd.doModal() == Dialog.OK ) {
		//			// subscribe user with content provider
		//			String user = ucd.getUsername();
		//			String pass = ucd.getPassword();
		//			// store user name
		//			PersistentStorage.setUsername( user );
		//			// perform the registration
		//			UnregisterCommand command = new UnregisterCommand( user, pass, false, PushConfig.isEnterprise() );
		//			executeCommand( command );
		//		}

		// Instead of getting user credentials from the dialog above, use pin (user=password):
		Logger.log("Starting unregister command");

		String userPass = Integer.toHexString(DeviceInfo.getDeviceId()).toUpperCase();
		PersistentStorage.setUsername( userPass );
		UnregisterCommand command = new UnregisterCommand( userPass, userPass, false, PushConfig.isEnterprise() );
		executeCommand( command );

		Logger.log("PUSH: successfully unregistered");

	}

	/**
	 * Suspend current registration
	 */
	public static void suspend() {
		// check that configuration is set and no command in progress
		if( commandInProgress() || invalidSettings() ) {
			return;
		}

		// get user credentials
		//		UserCredentialsDialog ucd = new UserCredentialsDialog( PersistentStorage.getUsername() );
		//		if( ucd.doModal() == Dialog.OK ) {
		//			// subscribe user with content provider
		//			String user = ucd.getUsername();
		//			String pass = ucd.getPassword();
		//			// store user name
		//			PersistentStorage.setUsername( user );
		//			// perform the registration
		//			SuspendCommand command = new SuspendCommand( user, pass, PushConfig.isEnterprise() );
		//			executeCommand( command );
		//		}
		// Instead of getting user credentials from the dialog above, use pin (user=password):
		String userPass = Integer.toHexString(DeviceInfo.getDeviceId()).toUpperCase();
		PersistentStorage.setUsername( userPass );
		SuspendCommand command = new SuspendCommand( userPass, userPass, PushConfig.isEnterprise() );
		executeCommand( command );
	}

	/**
	 * Resume current registration
	 */
	public static void resume() {
		// check that configuration is set and no command in progress
		if( commandInProgress() || invalidSettings() ) {
			return;
		}

		// get user credentials
		//		UserCredentialsDialog ucd = new UserCredentialsDialog( PersistentStorage.getUsername() );
		//		if( ucd.doModal() == Dialog.OK ) {
		//			// subscribe user with content provider
		//			String user = ucd.getUsername();
		//			String pass = ucd.getPassword();
		//			// store user name
		//			PersistentStorage.setUsername( user );
		//			// perform the registration
		//			ResumeCommand command = new ResumeCommand( user, pass, PushConfig.isEnterprise() );
		//			executeCommand( command );
		//		}
		// Instead of getting user credentials from the dialog above, use pin (user=password):
		String userPass = Integer.toHexString(DeviceInfo.getDeviceId()).toUpperCase();
		PersistentStorage.setUsername( userPass );
		ResumeCommand command = new ResumeCommand( userPass, userPass, PushConfig.isEnterprise() );
		executeCommand( command );	}

	//	public static void updateCountLabels() {
	//		int unreadCount = 0;
	//		PushMessage[] messages = PersistentStorage.getMessages();
	//		for( int i = messages.length - 1; i >= 0; i-- ) {
	//			if( messages[ i ].isUnread() ) {
	//				unreadCount++;
	//			}
	//		}
	//		int total = PersistentStorage.getTotalMessageCount();
	//		PushMain.getWelcomeScreen().setCount( total, unreadCount, PersistentStorage.getLastMessageReceived() );
	//	}

	public static void updateIndicator( boolean show ) {
		//# ifndef HANDHELD_VERSION_42
		// update indicator if device software is 4.6 or greater
		ApplicationIndicatorRegistry indicatorRegistry = ApplicationIndicatorRegistry.getInstance();
		ApplicationIndicator indicator = indicatorRegistry.getApplicationIndicator();
		if( indicator == null ) {
			ApplicationIcon icon = new ApplicationIcon( EncodedImage.getEncodedImageResource( "icon2_small.png" ) );
			indicator = indicatorRegistry.register( icon, false, true );
		} 
		if( show ) {
			indicator.setVisible( true );
		} else {
			indicator.setVisible( false );
		}
		//#endif
	}

	/**
	 * Called when SIM card has changed and our application was unsubscribed automatically by Push API
	 */
	public static void onSimChange() {
		Logger.log( "SIM card has changed. Automatically unregistering ..." );

		PersistentStorage.setRegistered( false );
		Controller.getInstance().setPushStatus(false);

		PushMessageListener listener = PushLibFactory.getPushMessageListener();
		listener.stopListening();

		Runnable uiUpdater = new Runnable() {
			public void run() {
				// SIM card has changed
				// this can happen if the BlackBerry device was given to another user
				UnregisterCommand command = new UnregisterCommand( null, null, true, PushConfig.isEnterprise() );
				executeCommand( command );

				// re-authorize the user and re-register if needed
				int registerAgain = Dialog.ask( Dialog.D_YES_NO, "The SIM card has changed and registration is being cancelled. "
						+ "Do you want to re-register for push messages?" );
				if( registerAgain == Dialog.YES ) {
					register();
				}
			}
		};
		PushUtils.runOnEventThread( uiUpdater );
	}

	/**
	 * Called when a new message is pushed
	 */
	public static void onMessage( PushMessage message ) {
		Logger.log("PUSH: onmessage was invoked");
		Logger.log("PUSH MESSAGE: "+new String(message.getData()));
		byte[] data = message.getData();

		//TEST
		Vector notifications = new PushMessageParser().parseXML(new String(data));
		if (!notifications.isEmpty()){
			Logger.log("adding new notifications");
			HomeScreen.updateIcon(Bitmap.getBitmapResource("icon2.png"));
			PushController.updateIndicator(true);
			Controller.getInstance().addNotifications(notifications);
			//TODO update grades if necessary
			Controller.getInstance().updateInstances();
			//if (n.getTipo()==2){
			//}
		}

		int size = data.length;
		if( message.isTextMesasge() ) {
			Logger.log( "Text message pushed, size " + size + " bytes" );
		} else {
			Logger.log( "Image message pushed, size " + size + " bytes" );
		}
		//		PersistentStorage.incTotalMessageCount();
		//		PushMessage[] messages = PersistentStorage.getMessages();
		//		int length = messages.length;
		//		PushMessage[] extended = new PushMessage[ length + 1 ];
		//		System.arraycopy( messages, 0, extended, 0, length );
		//		extended[ length ] = message;
		//		PersistentStorage.setMessages( extended );
		//
		//		Runnable uiUpdater = new Runnable() {
		//			public void run() {
		//				updateCountLabels();
		//				updateIndicator( 1 );
		//			}
		//		};
		//		PushUtils.runOnEventThread( uiUpdater );
	}

	/**
	 * Check whether application can quit or should remain running in the background
	 */
	public static boolean canQuit() {
		synchronized( cmdLock ) {
			if(currentCommand != null) {
				return false;
			}
		}

		PushMessageListener messageListener = PushLibFactory.getPushMessageListener();
		return messageListener.applicationCanQuit();
	}

	/**
	 * Called when application is started either by user or by Push API. If we registered for push messages then start the message
	 * listener thread.
	 */
	public static void appStarted() {

		// check whether this is the first application launch
		if( !PersistentStorage.isOnceLaunched() ) {
			PersistentStorage.setOnceLaunched( true );
			PushUtils.runOnEventThread( new Runnable() {
				public void run() {
					Logger.log( "Please register for push messages using application menu" );
				}
			} );
			return;
		}

		// the application may have been started by Push API when a message arrived
		// check the registration status and if registered then start listening thread
		if( PersistentStorage.isRegistered() ) {
			//if( Controller.getInstance().getPushStatus() ) {
			PushMessageListener messageListener = PushLibFactory.getPushMessageListener();
			messageListener.startListening();
		}

		// populate log screen
		//Logger.populateLogScreen();
	}

	/**
	 * Displays history of received push messages
	 */
	//	public static void showMessage( String msgId ) {
	//		PushMessage[] messages = PersistentStorage.getMessages();
	//		PushMessage pushMessage = null;
	//		for( int i = messages.length - 1; i >= 0; i-- ) {
	//			if( messages[ i ].getId().equals( msgId ) ) {
	//				pushMessage = messages[ i ];
	//				break;
	//			}
	//		}
	//
	//		if( pushMessage != null ) {
	//			// mark the message as read and update indicator and labels
	//			if( pushMessage.isUnread() ) {
	//				pushMessage.setUnread( false );
	//				PersistentStorage.setMessages( messages );
	//				updateCountLabels();
	//				updateIndicator( -1 );
	//			}
	//
	//			// show the message
	//			MessageViewScreen messageViewScreen = PushMain.getMessageViewScreen();
	//			byte[] data = pushMessage.getData();
	//			if( pushMessage.isTextMesasge() ) {
	//				String text;
	//				try {
	//					text = new String( data, "UTF-8" );
	//				} catch( Exception ex ) {
	//					text = new String( data );
	//				}
	//				messageViewScreen.setMessage( pushMessage.getTimestamp(), text );
	//				UiApplication.getUiApplication().pushScreen( messageViewScreen );
	//			} else {
	//				try {
	//					EncodedImage image = EncodedImage.createEncodedImage( data, 0, data.length );
	//					messageViewScreen.setMessage( pushMessage.getTimestamp(), image );
	//					UiApplication.getUiApplication().pushScreen( messageViewScreen );
	//				} catch( Exception ex ) {
	//					Dialog.alert( "Failed to convert image, caused by " + ex );
	//				}
	//			}
	//
	//			// refresh the history
	//			MessageHistoryScreen historyScreen = PushMain.getHistoryScreen();
	//			historyScreen.populate( PersistentStorage.getMessages(), msgId );
	//
	//		} else {
	//			RimLogger.warn( "Can't find message by ID: " + msgId );
	//		}
	//	}

	//	public static void clearMessageHistory() {
	//		PushMessage[] messages = new PushMessage[ 0 ];
	//		PersistentStorage.setMessages( messages );
	//		PersistentStorage.setTotalMessageCount( 0 );
	//		MessageHistoryScreen historyScreen = PushMain.getHistoryScreen();
	//		historyScreen.populate( messages, null );
	//		updateCountLabels();
	//		updateIndicator( 0 );
	//	}

	/**
	 * Displays history of received push messages
	 */
	//	public static void showMessages() {
	//		MessageHistoryScreen historyScreen = PushMain.getHistoryScreen();
	//		historyScreen.populate( PersistentStorage.getMessages(), null );
	//		UiApplication.getUiApplication().pushScreen( historyScreen );
	//	}

	/**
	 * Displays application log messages
	 */
	public static void showLog() {
		UiApplication.getUiApplication().pushScreen( PushMain.getLogScreen() );
	}

	/**
	 * Displays application configuration screen
	 */
	public static void showConfigScreen() {
		//UiApplication.getUiApplication().pushScreen( PushMain.getConfigScreen() );
	}

	/**
	 * Displays application configuration screen
	 */
	public static void cancelCurrentCommand() {
		synchronized( cmdLock ) {
			if( currentCommand != null ) {
				currentCommand.cancel();
				currentCommand = null;
			}
		}
		WelcomeScreen welcomeScreen = PushMain.getWelcomeScreen();
		welcomeScreen.setAction( null );
	}

	private static boolean invalidSettings() {
		String appId = PushConfig.getAppId();
		String cpUrl = PushConfig.getContentProviderUrl();
		String bpsUrl = PushConfig.getBpsUrl();
		boolean isEnterprise = PushConfig.isEnterprise();
		if( empty( appId ) || empty( cpUrl ) || ( ( !isEnterprise ) && empty( bpsUrl ) ) ) {
			Dialog.inform( "Some of the settings are empty or NULL.\nPlease make sure that all of the following are set"
					+ " in the application settings: application ID, content provider URL and BPS URL" );
			return true;
		}
		return false;
	}

	private static boolean empty( String s ) {
		return s == null || s.trim().length() == 0;
	}

	private static boolean commandInProgress() {
		NetworkCommand currentLocal;
		synchronized( cmdLock ) {
			currentLocal = currentCommand;
		}
		if( currentLocal == null ) {
			return false;
		}
		String commandName = currentLocal.getCommandName();
		int cancel = Dialog.ask( Dialog.D_YES_NO, "The BlackBerry smartphone is currently performing the following action - "
				+ commandName + ".\n Do you want to cancel it before executing new action?" );
		if( cancel == Dialog.YES ) {
			synchronized( cmdLock ) {
				if( currentLocal == currentCommand ) {
					currentCommand.cancel();
					currentCommand = null;
					return false;
				} else {
					return commandInProgress();
				}
			}
		} else {
			// user doesn't want to cancel current command
			return true;
		}
	}

	public static void executeCommand( NetworkCommand cmd ) {
		synchronized( cmdLock ) {
			if( currentCommand != null ) {
				currentCommand.cancel();
			}
			currentCommand = cmd;
			new Thread( cmd ).start();
		}
		WelcomeScreen welcomeScreen = PushMain.getWelcomeScreen();
		welcomeScreen.setAction( cmd.getCommandName() );
	}

	public static void commandExecuted( NetworkCommand cmd, boolean canceled, Exception error ) {
		NetworkCommand commandLocal = null;
		synchronized( cmdLock ) {
			if( currentCommand == cmd ) {
				commandLocal = currentCommand;
				currentCommand = null;
			}
		}
		if( commandLocal == null ) {
			return;
		}

		// check whether we need to listen for push messages
		PushMessageListener messageListener = PushLibFactory.getPushMessageListener();
		if( PersistentStorage.isRegistered() ) {
			messageListener.startListening();
		} else {
			messageListener.stopListening();
		}

		//WelcomeScreen welcomeScreen = PushMain.getWelcomeScreen();
		//welcomeScreen.setAction( null );

		boolean autoCommand = false;
		if(commandLocal instanceof UnregisterCommand) {
			UnregisterCommand unregisterCommand = (UnregisterCommand) commandLocal;
			autoCommand = unregisterCommand.isOnSimSwap();
		}

		if( !canceled && !autoCommand ) {
			String name = commandLocal.getCommandName();
			// notify the user
			if( error == null ) {
				Logger.log("PUSH command: Request to " + name + " executed successfully" );
				//Dialog.inform( "Request to " + name + " executed successfully" );
			} else {
				Logger.log("Push command: Request to " + name + " failed. Caused by " + error );
				//Dialog.inform( "Request to " + name + " failed. Caused by " + error );
				Controller.getInstance().setPushStatus(false);
			}
		}
	}

	public static void onPortListenError( Exception error ) {
		Dialog.alert( "Can not listen for push messages. "
				+ "Network port may be used by another application or blocked by firewall. "
				+ "You will NOT receive messages. Details: " + error );
	}
}
